#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <util.h>
#include <general.h>

#define MAX_QUEUE_BUF_SIZE (1024)

pthread_mutex_t fd_mutex;
char *fd_in_use;
int  *fds;

/*
int *alloc_fds(int num) {
    int *fd_l = (int *) malloc(num * sizeof(int));
    if (fd_l == NULL) {
        perror("alloc fds fail");
        exit(1);
    }
    return fd_l;
}

void release_fds(int *fd_list) {
    if (NULL!= fd_list) {
        free(fd_list);
    }
}
 */

/* Grabs an unused file descriptor */
int unused_fd(int num_conn) {
    int fd;

    pthread_mutex_lock(&fd_mutex);
    do {
        fd = fds[random() % num_conn];
    } while (fd_in_use[fd]);

    fd_in_use[fd]++;
    pthread_mutex_unlock(&fd_mutex);

    return fd;
}

/* Releases a file descriptor */
int release_fd(int fd) {
    pthread_mutex_lock(&fd_mutex);
    fd_in_use[fd] = 0;
    pthread_mutex_unlock(&fd_mutex);
    return 0;
}

void ensure_readable(int fd, int poll_wait_second)
{
    if (! waitread(fd, poll_wait_second)) {
        fprintf(stderr, "\nfd %d went %d sec with no reply!\n",
                fd, poll_wait_second);
        exit(1);
    }
}

void init_server_fds(int num, int *fd_list) {
	int i=0;
    for ( i=0; i<num; i++) {
        fd_list[i] = clientsock(s_config.mc_servers[i], s_config.mc_server_ports[i]);
        if (fd_list[i] < 0) {
            perror("create socket fail");
            exit(1);
        } else {
            fprintf(stderr, "create socket=%d (server=%s port=%d fd=%d)\n",
            i, s_config.mc_servers[i], s_config.mc_server_ports[i], fd_list[i]);
        }
        //set_nodelay(server_fds[i], 1);
        // set_nonblock(server_fds[i][j]);
    }
    fprintf(stderr, "finish create %d connections\n", num);
}

/*
void init_proxy_conn(int num_conn, int *server_fds, int proxy_fd) {
    proxy_conn.conn_list = (proxy_conn_info **)malloc(num_conn * sizeof(proxy_conn_info *));
    if (NULL == proxy_conn.conn_list) {
        exit(EXIT_FAILURE);
    }
    proxy_conn_info *pc;
    for (int i=0; i<num_conn; i++) {
        proxy_conn.conn_list[i] = (proxy_conn_info *)malloc(sizeof(proxy_conn_info));
        if (NULL == proxy_conn.conn_list[i])
            exit(EXIT_FAILURE);

        pc = proxy_conn.conn_list[i];
        pc->conn.server = s_config.mc_servers[i];
        pc->conn.port   = s_config.mc_server_ports[i];
        pc->conn.rank   = i+1;
        pc->conn.sfd    = server_fds[i];

        pc->sndbuf = (char *)malloc(sizeof(char) * MAX_GET_BUF_SIZE);
        pc->sndbufsize = MAX_GET_BUF_SIZE;
        pc->rcvbuf = (char *)malloc(sizeof(char) * MAX_GET_BUF_SIZE);
        pc->rcvbufsize = MAX_GET_BUF_SIZE;
        pc->sndlen = pc->sndoff = pc->rcvnext = pc->rcvstart = 0;

        if (NULL == pc->sndbuf || NULL == pc->rcvbuf)
            exit(EXIT_FAILURE);

        fprintf(stderr, "proxy_conn[%d] server=%s port=%d rank=%d sfd=%d\n",
                i, pc->conn.server, pc->conn.port, pc->conn.rank, pc->conn.sfd);
    }
    proxy_conn.num_conn = num_conn;
    proxy_conn.proxy_fd = proxy_fd;
    fprintf(stderr, "init_proxy_conn num=%d proxy_fd=%d\n",
            proxy_conn.num_conn, proxy_conn.proxy_fd);
}

void release_proxy_conn(int num_conn) {
    if (NULL != proxy_conn.conn_list) {
        proxy_conn_info *pc;
	int i=0;
        for ( i = 0; i < num_conn; i++) {
            pc = proxy_conn.conn_list[i];
            if (NULL != pc) {
                if (NULL != pc->sndbuf) {
                    free(pc->sndbuf);
                    pc->sndbuf = NULL;
                }
                if (NULL != pc->rcvbuf) {
                    free(pc->rcvbuf);
                    pc->rcvbuf = NULL;
                }
                free(pc);
            }
        }
        free(proxy_conn.conn_list);
    }
}
 */

void init_proxy_conn_queue_buf(queue_buf *buf) {
    buf->buf = (char *)malloc(sizeof(char)*MAX_QUEUE_BUF_SIZE);
    buf->buf_len = MAX_QUEUE_BUF_SIZE;
    buf->buf_offset = 0;
    buf->num_rqs = 0;
    if (NULL == buf->buf)
        exit(EXIT_FAILURE);
}

void free_proxy_conn_queue_buf(queue_buf *buf) {
    if (buf->buf != NULL) {
        free(buf->buf);
    }
    buf->buf = NULL;
}

void init_pcs_conn(conn_info *info, int idx) {
    int fd = clientsock(s_config.mc_servers[idx], s_config.mc_server_ports[idx]);
    if (fd < 0) {
        perror("create socket fail");
        exit(EXIT_FAILURE);
    } else {
        fprintf(stderr, "create socket=%d (server=%s port=%d fd=%d)\n",
                idx, s_config.mc_servers[idx], s_config.mc_server_ports[idx], fd);
    }
    //set_nodelay(fd, 1);
    //set_nonblock(fd);
    info->server = s_config.mc_servers[idx];
    info->port   = s_config.mc_server_ports[idx];
    info->rank   = idx;
    info->sfd    = fd;
}

void init_proxy_conn_event(event_context *ctx, event_context *e) {
    ctx->base = e->base;
    ctx->event_read = e->event_read;
    ctx->event_write = e->event_write;
}

void init_proxy_conn_client_entry(proxy_conn_client_list *pcc, int fd, int port, char *server) {
    pcc->pcc_list[pcc->num_c_conn] = (conn_info *)malloc(sizeof(conn_info));
    conn_info *p = pcc->pcc_list[pcc->num_c_conn];
    p->rank = pcc->num_c_conn;
    p->sfd = fd;
    p->port = port;
    p->server = server;
    pcc->num_c_conn += 1;
}

void init_proxy_conn_server_list(proxy_conn_server_list *pcs, server_config *config) {
    pcs->num_s_conn = config->num_servers;
    pcs->pcs_list = (proxy_conn_w_buf **)malloc(pcs->num_s_conn * sizeof(proxy_conn_w_buf *));
    if (NULL == pcs->pcs_list) {
        exit(EXIT_FAILURE);
    }
    proxy_conn_w_buf *p;
	int i=0;
    for ( i=0; i<pcs->num_s_conn; i++) {
        pcs->pcs_list[i] = (proxy_conn_w_buf *) malloc(sizeof(proxy_conn_w_buf));
        if (NULL == pcs->pcs_list[i])
            exit(EXIT_FAILURE);

        p = pcs->pcs_list[i];

        init_proxy_conn_queue_buf(&p->q_buf);
        //cps->q_buf.buf = (char *)malloc(sizeof(char)*MAX_QUEUE_BUF_SIZE);
        //cps->q_buf.buf_len = MAX_QUEUE_BUF_SIZE;
        //cps->q_buf.buf_offset = 0;
        //cps->q_buf.num_rqs = 0;

        //cps->conn_ctx.event_ctx.base = e_ctx->base;
        //cps->conn_ctx.event_ctx.event_read = e_ctx->event_read;
        //cps->conn_ctx.event_ctx.event_write = e_ctx->event_write;

        init_pcs_conn(&p->conn, i);
        //cps->conn_ctx.conn.server = s_config.mc_servers[i];
        //cps->conn_ctx.conn.port   = s_config.mc_server_ports[i];
        //cps->conn_ctx.conn.rank   = i+1;
        //cps->conn_ctx.conn.sfd    = server_fds[i];

        fprintf(stderr, "proxy_conn_server[%d] server=%s port=%d rank=%d sfd=%d\n",
                i, p->conn.server, p->conn.port, p->conn.rank, p->conn.sfd);
    }
    fprintf(stderr, "init_proxy_conn_server num=%d\n", pcs->num_s_conn);
}

void release_proxy_conn_server_list(proxy_conn_server_list *pcs, int num_conn) {
    if (NULL != pcs->pcs_list) {
        proxy_conn_w_buf *p;
	int i=0;
        for ( i = 0; i < num_conn; i++) {
            p = pcs->pcs_list[i];
            if (NULL != p) {
                free_proxy_conn_queue_buf(&p->q_buf);
                //if (NULL != cps->q_buf.buf) {
                //    free(cps->q_buf.buf);
                //    cps->q_buf.buf = NULL;
                //}
                free(p);
            }
        }
        free(pcs->pcs_list);
    }
}

